/**
 * Copyright (c) 2018-2022, NXOS Development Team
 * SPDX-License-Identifier: Apache-2.0
 * 
 * Contains: device vfs interface.
 * 
 * Change Logs:
 * Date           Author            Notes
 * 2022-4-19      JasonHu           Init
 */

// #ifdef CONFIG_NX_FS_DEVFS
#if 1

#include <nxos.h>

#include "../vfs_server.h"

#undef NX_ErrorSet
#define NX_ErrorSet(errPtr, errVal) \
    do { \
        if (errPtr) \
        { \
            *(errPtr) = errVal; \
        } \
    } while(0)

NX_PRIVATE NX_Error DevMount(NX_MountDesc * m, const char * dev)
{
	m->root->data = (void *)NX_NULL;
	m->data = NX_NULL;
	return NX_EOK;
}

NX_PRIVATE NX_Error DevUnmount(NX_MountDesc * m)
{
	m->data = NX_NULL;
	return NX_EOK;
}

NX_PRIVATE NX_Error DevMountSync(NX_MountDesc * m)
{
	return NX_EOK;
}

NX_PRIVATE NX_Error DevGetNode(NX_MountDesc * m, NX_FileNode * n)
{
	n->data = (void *)NX_SOLT_INVALID_VALUE;	
	// NX_Printf("devfs: DevGetNode: %p\n", n);
	return NX_EOK;
}

NX_PRIVATE NX_Error DevPutNode(NX_MountDesc * m, NX_FileNode * n)
{
	NX_Solt solt = (NX_Solt)n->data;
	if (solt != NX_SOLT_INVALID_VALUE)
	{
		// NX_Printf("devfs: DevPutNode: %p solt:%d\n", n, solt);
		NX_DeviceClose(solt);
	}
	return NX_EOK;
}

NX_PRIVATE NX_U64 DevRead(NX_FileNode * n, NX_I64 off, void * buf, NX_U64 len, NX_Error *outErr)
{
	NX_Solt dev = (NX_Solt)n->data;
	// NX_Printf("devfs: read %d: %s\n", dev, buf);

	NX_Size outLen = NX_DeviceRead(dev, buf, off, len);
	if (outErr) {
		*outErr = NX_ErrorGet();
	}
	// NX_Printf("devfs: read len %d\n", outLen);
	return outLen;
}

NX_PRIVATE NX_U64 DevWrite(NX_FileNode * n, NX_I64 off, void * buf, NX_U64 len, NX_Error *outErr)
{
	NX_Solt dev = (NX_Solt)n->data;

	// NX_Printf("devfs: write %d: %s\n", dev, buf);

	NX_Size outLen = NX_DeviceWrite(dev, buf, off, len);
	if (outErr) {
		*outErr = NX_ErrorGet();
	}
	// NX_Printf("devfs: write len: %d\n", outLen);
	return outLen;
}

NX_PRIVATE NX_Error DevIoctl(NX_FileNode * n, NX_U32 cmd, void *arg)
{
    NX_Solt dev = (NX_Solt)n->data;

    return NX_DeviceControl(dev, cmd, arg);
}

NX_PRIVATE NX_Error DevTruncate(NX_FileNode * n, NX_I64 off)
{
	return NX_EPERM; /* can't truncate */
}

NX_PRIVATE NX_Error DevSync(NX_FileNode * n)
{
	return NX_EOK;
}

NX_PRIVATE NX_Error DevReadDir(NX_FileNode * dn, NX_I64 off, NX_Dirent * d)
{
	NX_Error err;
    NX_DeviceInfo devinfo;

	/**
	 * 枚举设备信息，给一个偏移，返回每一个设备的信息。设备名字。
	 */
	err = NX_DeviceEnum(off, &devinfo);
	if (err != NX_EOK)
    {
        return NX_ENORES;
    }

    d->type = NX_DIR_TYPE_REG;
    
	NX_StrCopyN(d->name, devinfo.name, sizeof(d->name));
    d->name[sizeof(devinfo.name) - 1] = '\0';

	d->off = off;
	d->reclen = 1;

	// NX_Printf("devfs: read dir: %s\n", devinfo.name);

    return NX_EOK;
}

NX_PRIVATE NX_Error DevLookup(NX_FileNode * dn, const char * name, NX_FileNode * n)
{
	NX_Error err;
    NX_DeviceInfo devinfo;
	NX_Solt solt;

	/**
	 * 查询设备的信息，设备名，设备模式等信息。
	 */
	err = NX_DeviceProbe(name, &devinfo);
	if (err != NX_EOK)
	{
		return NX_ENOSRCH;
	}

	/**
	 * open device
	 */
	solt = NX_DeviceOpen(name, 0);
	if (solt == NX_SOLT_INVALID_VALUE)
	{
		return NX_ENORES;
	}

	// NX_Printf("devfs: lookup: %s, solt:%d\n", devinfo.name, solt);

	n->atime = 0;
	n->mtime = 0;
	n->ctime = 0;
	n->mode = 0;
	n->size = 0;
	n->data = (void *)solt;

	n->type = NX_FILE_NODE_TYPE_REG;
	n->mode |= NX_FILE_TYPE_REG;
	if(devinfo.mode & 0x01)
	{
		n->mode |= (NX_FILE_MODE_READ);
	}
	if(devinfo.mode & 0x02)
	{
		n->mode |= (NX_FILE_MODE_WRITE);
	}

	return NX_EOK;
}

NX_PRIVATE NX_Error DevCreate(NX_FileNode * dn, const char * filename, NX_U32 mode)
{
	return NX_EPERM;
}

NX_PRIVATE NX_Error DevDelete(NX_FileNode * dn, NX_FileNode * n, const char *name)
{
	return NX_EPERM;
}

NX_PRIVATE NX_Error DevRename(NX_FileNode * sn, const char * sname, NX_FileNode * n, NX_FileNode * dn, const char * dname)
{
	return NX_EPERM;
}

NX_PRIVATE NX_Error DevCreateDir(NX_FileNode * dn, const char * name, NX_U32 mode)
{
	return NX_EPERM;
}

NX_PRIVATE NX_Error DevDeleteDir(NX_FileNode * dn, NX_FileNode * n, const char *name)
{
	return NX_EPERM;
}

NX_PRIVATE NX_Error DevSetMode(NX_FileNode * n, NX_U32 mode)
{
	return NX_EPERM;
}

NX_PRIVATE NX_FileSystem devfs = {
	.name		= "devfs",
	.mount		= DevMount,
	.unmount	= DevUnmount,
	.mountSync	= DevMountSync,
	.getNode	= DevGetNode,
	.putNode	= DevPutNode,
	.read		= DevRead,
	.write		= DevWrite,
    .ioctl      = DevIoctl,
	.truncate	= DevTruncate,
	.sync		= DevSync,
	.readDir	= DevReadDir,
	.lookup		= DevLookup,
	.create		= DevCreate,
	.delete		= DevDelete,
	.rename		= DevRename,
	.createDir	= DevCreateDir,
	.deleteDir	= DevDeleteDir,
	.setMode	= DevSetMode,
};

void NX_FileSystemDevInit(void)
{
	NX_FileSystemRegister(&devfs);
    // NX_LOG_I("register devfs done.");
}

void NX_FileSystemDevExit(void)
{
	NX_FileSystemUnregister(&devfs);
}

// NX_FS_INIT(NX_FileSystemDevInit);
// NX_FS_EXIT(NX_FileSystemDevExit);

#endif /* CONFIG_NX_FS_DEVFS */
